package com.maybach.web.framework.util;

import com.maybach.redis.IRedisRepository;
import com.maybach.util.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
 * CSRF tool, 获取csrf token。
 */
public class CSRFTool {

    /**
     * The token parameter name
     */
    public static final String CSRF_PARAM_NAME = "_csrf_token_";

    /**
     * crsf token 过期时间为30分，（单位：毫秒）。
     */
    public static final long CSRF_TOKEN_EXPIRE_TIME_MILLIS = 1800000;

    /**
     * The location on the session which stores the token
     */
    public static final String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = "csrf_token";

    /**
     * token name 分隔符.
     */
    public static final String TOKEN_NAME_SEPARATE_CHAR = "#";


    /**
     * redis 操作类 ferrariRedisRepository.
     */
    static private IRedisRepository redisRepository;

    private static final Logger logger = LoggerFactory.getLogger(CSRFTool.class);

    /**
     * get token from cache.
     *
     * @param request   HttpServletRequest
     * @param tokenName CSRF token name.
     * @return
     */
    public static String getTokenFromCache(HttpServletRequest request, String tokenName) {
        redisRepository = getRedisRepository();
        String token = null;
        try {
            String key = getKey(request, tokenName);
            token = (String) redisRepository.get(key);
            if (StringUtils.isNotBlank(token)) {
                return token;
            }
            token = UUID.randomUUID().toString();
            if (StringUtils.isNotBlank(tokenName)) {
                token += TOKEN_NAME_SEPARATE_CHAR + tokenName;
            }
            redisRepository.set(key, token, CSRF_TOKEN_EXPIRE_TIME_MILLIS);
        } catch (Exception e) {
            logger.error("get csrf token error.", e);
        }
        return token;
    }

    /**
     * clear token in cache.
     *
     * @param request   HttpServletRequest
     * @param tokenName csrf token名称
     */
    public static void clearTokenFromCache(HttpServletRequest request, String tokenName) {
        redisRepository = getRedisRepository();
        if (redisRepository == null) {
            return;
        }
        try {
            String key = getKey(request, tokenName);
            redisRepository.delete(key);
        } catch (Exception e) {
            logger.error("remove csrf token error. tokenName = " + tokenName, e);
        }
    }

    /**
     * Extracts the token value from the session
     *
     * @param request
     * @return
     */
    public static String getTokenFromRequest(HttpServletRequest request) {
        String token = request.getParameter(CSRF_PARAM_NAME);
        if (token == null || "".equals(token)) {
            token = request.getHeader(CSRF_PARAM_NAME);
        }
        return token;
    }

    /**
     * 获取csrf token key。
     *
     * @param request   HttpServletRequest
     * @param tokenName tokenName
     * @return
     */
    private static String getKey(HttpServletRequest request, String tokenName) {
        HttpSession session = request.getSession();
        String key = CSRF_TOKEN_FOR_SESSION_ATTR_NAME;
        key += ":" + session.getId();
        if (StringUtils.isNotBlank(tokenName)) {
            key += TOKEN_NAME_SEPARATE_CHAR + tokenName.trim();
        }
        return key;
    }


    /**
     * 获取redis操作类 IRedisRepository
     */
    private static IRedisRepository getRedisRepository() {

        if (redisRepository == null) {
            redisRepository = SpringContextUtil.getBean(IRedisRepository.class);
        }

        return redisRepository;
    }

    /**
     * 获取csrf token。
     *
     * @param request
     * @param tokenName
     * @return
     */
    public static String getToken(HttpServletRequest request, String tokenName) {
        return getTokenFromCache(request, tokenName);
    }


    /**
     * 获取csrf token。
     *
     * @param request
     * @return
     */
    public static String getToken(HttpServletRequest request) {
        return getToken(request, null);
    }

}
